<template>
  <div class="pl-3 pr-3">
    <teleport to="body">
      <button
        v-if="busEntity"
        class="absolute top-[30px] right-[385px] text-white bg-deepblue p-2.5 rounded-md border-lightblue border"
        @click="showBubble"
      >
        显示公交信息弹框
      </button>
    </teleport>
    <div class="controlers flex justify-between items-center">
      <!-- 暂停 -->
      <span v-if="!data.isStop" class="icons relative">
        <Icon
          @click="toPlayOrStop"
          icon="ep:video-pause"
          width="20"
          height="20"
          class="cursor-pointer hover:text-myblue"
        />
        <SmallTip class="tips" top="10">暂停</SmallTip>
      </span>

      <!-- 播放 -->
      <span v-else class="icons relative">
        <Icon
          @click="toPlayOrStop"
          icon="ep:video-play"
          width="20"
          height="20"
          class="cursor-pointer hover:text-[rgb(30,64,175)]"
        />
        <SmallTip class="tips" top="10">播放</SmallTip>
      </span>

      <!-- 减速 -->
      <span class="icons relative">
        <Icon
          @click="changeSpeed('sub')"
          icon="ep:d-arrow-left"
          width="20"
          height="20"
          class="cursor-pointer hover:text-myblue"
          :class="{
            'text-slate-400': !canSubSpeed,
            'hover:text-slate-400': !canSubSpeed,
            'cursor-not-allowed': !canSubSpeed,
          }"
        />
        <SmallTip class="tips">减速</SmallTip>
      </span>

      <!-- 加速 -->
      <span class="icons relative">
        <Icon
          @click="changeSpeed('add')"
          icon="ep:d-arrow-right"
          width="20"
          height="20"
          class="cursor-pointer hover:text-myblue"
          :class="{
            'text-slate-400': !canAddSpeed,
            'hover:text-slate-400': !canAddSpeed,
            'cursor-not-allowed': !canAddSpeed,
          }"
        />
        <SmallTip class="tips">加速</SmallTip>
      </span>

      <!-- 重置 -->
      <span class="icons relative">
        <Icon
          icon="ep:refresh"
          width="20"
          height="20"
          class="cursor-pointer hover:text-myblue"
          @click="reset"
        />
        <SmallTip class="tips">重置</SmallTip>
      </span>

      <!-- 跟随视角 -->
      <button class="btns" @click="changeView('follow')">跟随视角</button>
      <!-- 车内视角 -->
      <button class="btns" @click="changeView('inCar')">车内视角</button>
      <!-- 自由视角 -->
      <button class="btns" @click="changeView('free')">自由视角</button>
      <!-- 速度 -->
      <span class="speed">
        <span class="text-xl">{{ data.speed }}</span>
        <span style="font-size: 10px">m/s</span>
      </span>
    </div>
    <div class="bar">
      <div class="bar_outer mt-2.5 rounded-[7px] bg-white">
        <!-- 无法识别 -->
        <!-- :class="`w-[${data.barPercent * 100}%]`" -->
        <div
          class="bar_inner rounded-[7px] bg-sky-400 h-[15px]"
          :style="{ width: `${data.barPercent * 100}%` }"
        ></div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { reactive, inject, onMounted, computed, watch } from "vue";
import { Icon } from "@iconify/vue";
import * as Cesium from "cesium";
import {
  getSiteTimes,
  computeCirclularFlight,
  spaceDistance,
} from "/src/tools/trajectory";
import SmallTip from "/src/components/SmallTip";
import $bus from "/src/bus";
import Bubble from "/src/tools/bubbleCar";
// 来自于card组件的provide
const $getActiveIndex = inject("$getActiveIndex");
const $changeActiveIndex = inject("$changeActiveIndex");
const $datas = inject("$datas");
let busEntity = null;
const data = reactive({
  isStop: true, // 是否处在播放暂停状态
  speed: 15,
  startPlay: false, // 是否开始了第一次播放
  maxSpeed: 160,
  minSpeed: 5,
  barPercent: 0,
  carInfo: $datas,
});

const canAddSpeed = computed(() => data.speed !== data.maxSpeed);
const canSubSpeed = computed(() => data.speed !== data.minSpeed);
let $viewer = null;
let $positions = [];
let timeSum,
  siteTimes,
  start,
  stop,
  stationTimer,
  isInCar = false,
  bubble;
const getShouldAnimate = () => $viewer.clock.shouldAnimate;
onMounted(() => {
  $viewer = inject("$viewer");
  $positions = inject("$positions");
  setAnimationTimeBoundary();
});
function setAnimationTimeBoundary() {
  // 总时间 // 到达每一个站点的时间组成的数组
  const timeObj = getSiteTimes($positions, data.speed);
  timeSum = timeObj.timeSum;
  siteTimes = timeObj.siteTimes;
  // 创建一个JulianDate对象，表示日期和时间为"2015-03-25 16:00"。
  start = Cesium.JulianDate.fromDate(new Date());
  start = Cesium.JulianDate.addHours(start, 8, new Cesium.JulianDate()); // 开始时间加8小时改为北京时间
  // 将120秒（2分钟）添加到 'start' 的JulianDate上，得到一个新的JulianDate，表示稍后的时间。
  stop = Cesium.JulianDate.addSeconds(
    // 结束时间为开始时间加timeSum秒
    start,
    timeSum,
    new Cesium.JulianDate() // 一个可选的结果参数（如果未提供，则创建新的JulianDate）
  );
  // 时间轴绑定到viewer上去
  // $viewer.timeline.zoomTo(start, stop);
  // 将视图器的动画时钟(zuo下角)的起始时间设置为 'start' 的克隆。
  $viewer.clock.startTime = start.clone();
  // 将视图器的时钟的停止时间设置为 'stop' 的克隆。
  $viewer.clock.stopTime = stop.clone();
  // 将视图器的时钟的当前时间设置为 'start' 的克隆。【下方时间轴当前时间的所在位置】
  $viewer.clock.currentTime = start.clone();
  // 将视图器的时钟范围设置为 LOOP_STOP，达到终止时间后重新循环。
  $viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP;
  // 不改变真正的行驶速度，而是设置时钟的multiplier播放速度来设置初始的视觉速度
  $viewer.clock.multiplier = (data.speed / 15) * 1;
}
// 播放或停止按钮
function toPlayOrStop() {
  data.isStop = !data.isStop;
  if (!data.startPlay) {
    // 如果没开始第一次播放，就开始第一次播放
    data.startPlay = true;
    // 启用视图器的时钟动画。
    $viewer.clock.shouldAnimate = true;
    // 生成时间插值采样属性(SampledPositionProperty 对象)
    let property = computeCirclularFlight($positions, start, siteTimes);
    // console.log(property) // property._property._times property._property._values
    // 332个时间点和332个笛卡尔坐标
    // 添加需要沿着多线段漫游的公交车模型实体，在添加模型实体时，将property样本位置属性也添加进去
    // 从而形成一个沿着时间变化自动改变位置的公交车模型漫游实体
    busEntity = $viewer.entities.add({
      availability: new Cesium.TimeIntervalCollection([
        new Cesium.TimeInterval({
          start: start,
          stop: stop,
        }),
      ]),
      model: {
        uri: "/model/bus.gltf",
        minimumPixelSize: 40,
      },
      // viewFrom: new Cesium.Cartesian3(-100.0, 0.0, 100.0),// 初始的时候从哪个视图去看
      position: property, // 位置属性是一个随时间变化的位置属性
      orientation: new Cesium.VelocityOrientationProperty(property),
    });
    $bus.emit("$getBusEntity", busEntity);
    // 轨迹追踪视图
    $viewer.trackedEntity = busEntity;
    // 给时钟添加监听事件，到站点就停+时刻改变进度条
    $viewer.clock.onTick.addEventListener(tickEventHandler);
  } else if (data.isStop === true) {
    // 如果想要暂停播放
    $viewer.clock.shouldAnimate = false;
    // 如果正停止车站，清除定时器(5s后不再继续走)
    if (stationTimer) clearTimeout(stationTimer);
  } else {
    // 如果想要继续播放
    $viewer.clock.shouldAnimate = true;
  }
}
// 变速按钮
function changeSpeed(addOrsub) {
  if (
    (data.speed === data.minSpeed && addOrsub === "sub") ||
    (data.speed === data.maxSpeed && addOrsub === "add")
  ) {
    return;
  }
  data.speed = addOrsub === "add" ? data.speed + 5 : data.speed - 5;
  $viewer.clock.multiplier = (data.speed / 15) * 1;
}
// 获取busEntity的中心实时位置
function getCenterPosition() {
  let position = busEntity.position.getValue($viewer.clock.currentTime);
  // 因为车辆路线的这一条多线段在数据信息中只给了我们经纬度坐标，所以在转笛卡尔的时候默认高度是0的
  // 为了让相机看向的是车辆的中部而非车底
  // 把position转换回弧度经纬制图坐标，添加一个高度（模型高度的一半）
  // 然后再转换回笛卡尔坐标，作为newPosition
  const cartog = Cesium.Cartographic.fromCartesian(position);
  // console.log(cartog)
  cartog.height = 1.8;
  const newPosition = Cesium.Cartographic.toCartesian(cartog);
  return newPosition;
}
// 时钟滴答事件
function tickEventHandler() {
  if ($viewer.clock.shouldAnimate === false) return;
  // 实时获取busEntity的位置和朝向
  let ori = busEntity.orientation.getValue($viewer.clock.currentTime);
  // console.log(position, ori);
  // console.log(ori);
  const newPosition = getCenterPosition();
  if (bubble && bubble.vmInstance) {
    bubble.position = newPosition;
  }
  // console.log("tick()");
  // return;
  // 计算当前时间与开始时间的时间差(单位是s)
  const timeGap = Cesium.JulianDate.secondsDifference(
    $viewer.clock.currentTime,
    $viewer.clock.startTime
  );
  // console.log(timeGap);
  data.barPercent = timeGap / timeSum;
  // 时钟每滴答一次就调用一次tickEventHandler回调函数
  // 计算当前汽车模型的位置与当前要前往的站点模型的位置之间的距离
  let index = $getActiveIndex();
  const id = index === $datas.stations.length - 1 ? 0 : index + 1;
  // console.log(busEntity.position)// SamplePositionProperty{}
  const currentPosition = busEntity.position.getValue(
    $viewer.clock.currentTime
  );
  const stationPosition = $viewer.entities
    .getById("station" + id)
    .position.getValue($viewer.clock.currentTime);
  const distance = spaceDistance([currentPosition, stationPosition]);
  if (distance < 10) {
    $viewer.clock.shouldAnimate = false;
    $changeActiveIndex(index === $datas.stations.length - 1 ? 0 : ++index);

    stationTimer = setTimeout(() => {
      $viewer.clock.shouldAnimate = true;
      data.isStop = false;
      stationTimer = null;
    }, 5000);
  }
  // console.log(ori);
  if (isInCar) {
    // console.log(ori);
    // console.log(isInCar)
    toInCar(newPosition, ori);
  }
}
watch($getActiveIndex, () => {
  setTimeout(() => {
    data.carInfo.peopleNum = Math.ceil(Math.random(30 - 20 + 1)) + 10;
  }, 5000);
});
// 重置
function reset() {
  $viewer.clock.shouldAnimate = false;
  data.isStop = true;
  data.startPlay = false;
  $viewer.clock.multiplier = 1;
  data.speed = 15;
  removeEvent();
  $viewer.clock.currentTime = start;
  data.barPercent = 0;
  if (stationTimer) clearTimeout(stationTimer);
  stationTimer = null;
  $changeActiveIndex(0);
  changeView("free");
  if (busEntity) $viewer.entities.remove(busEntity);
  busEntity = null;
  $bus.emit("getBusEntity", busEntity);
  $viewer.flyTo($viewer.entities, {
    duration: 2,
  });
}
// 去掉时钟滴答事件的监听
function removeEvent() {
  $viewer.clock.onTick.removeEventListener(tickEventHandler);
}
// 切换视角
function changeView(view) {
  if (!busEntity) return;
  switch (view) {
    case "follow":
      isInCar = false;
      $viewer.trackedEntity = busEntity;
      break;
    case "inCar":
      $viewer.trackedEntity = null;
      isInCar = true;
      break;
    case "free":
      isInCar = false;
      $viewer.trackedEntity = null;
      // 初始化为单位矩阵的不可变 Matrix4 实例。
      // 将相机的视角重置为一个没有任何变换的状态，即相机位于默认位置、方向和上向量
      $viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY);
      break;
  }
}

// 车内视角的函数
const toInCar = (newPosition, ori) => {
  if (busEntity) {
    // 利用模型的偏向角和实时位置，转化成一个包含模型位置和方向的4*4矩阵
    // //将偏向角转为3*3矩阵，实时点加偏向角的3*3矩阵，转为4*4矩阵
    // 这个transform描述模型在三维空间中的位置和朝向
    let transform = Cesium.Matrix4.fromRotationTranslation(
      Cesium.Matrix3.fromQuaternion(ori), // 将四元数 ori 转换为旋转矩阵
      newPosition //  newPosition（笛卡尔坐标）
    );
    /* $viewer.camera.lookAt(
      // 相机看向transform这个包含位置和朝向的4*4矩阵
      newPosition,
      // new Cesium.Cartesian3(-0.0001, 0, -0.00004)
      new Cesium.Cartesian3(100, 100, 100) // 这里的相对newPosition移动是在世界坐标的笛卡尔坐标系中移动了这么多，不会在车转弯时自动换方向
      // 并将将相机在这个矩阵坐标系中往后移动一点点
    ); */
    $viewer.camera.lookAtTransform(
      // 相机看向transform这个包含位置和朝向的4*4矩阵
      transform,
      // new Cesium.Cartesian3(-0.0001, 0, -0.00004)
      new Cesium.Cartesian3(-3, 0, 0)
      // 并将将相机在这个矩阵坐标系中往后移动一点点
    ); // 这里的相对transform(包括位置和朝向形成的平移旋转矩阵4)移动的参考坐标系不是世界坐标系
    // 而是模型坐标系（或者说是这个平移旋转矩阵的参考坐标系）这时第二个参数笛卡尔坐标的移动是
    // 相对于这个模型坐标系移动的
    // 以上两种方法都表示从第二个参数的偏移后的点为相机点，看向第一个参数的目标点
    return newPosition;
  } else {
    return;
  }
};

// 显示公交信息
function showBubble() {
  if ((bubble && bubble.vmInstance) || !busEntity) return;
  bubble = new Bubble({
    viewer: $viewer,
    carInfo: data.carInfo,
    shouldAnimate: getShouldAnimate,
  });
  const newPosition = getCenterPosition();
  if (bubble && bubble.vmInstance) {
    bubble.position = newPosition;
    bubble.postRender();
  }
}

// 当浏览器切到别的页面时，监听器就会停止对viewer.clock.onTick事件的监听
// 'visibilitychange' 事件，当浏览器的可见性状态发生改变时，在document元素上触发
document.addEventListener("visibilitychange", () => {
  if (document.visibilityState === "hidden") {
    // 页面变为不可见时的逻辑
    $viewer.clock.shouldAnimate = false;
    // 如果正停在车站，清除定时器(5s后不再继续走)
    if (stationTimer) clearTimeout(stationTimer);
    stationTimer = null;
    data.isStop = true;
  } else if (document.visibilityState === "visible") {
    // 页面变为可见时的逻辑
    // if (!busEntity) return
    // $viewer.clock.shouldAnimate = true;
  }
});
</script>

<style lang="scss" scoped>
.icons {
  &:hover {
    .tips {
      display: block;
    }
  }
}
.btns {
  background-color: #2e9bff;
  color: #fff;
  border-radius: 3px;
  padding: 3px;
  padding-inline: 5px;
  vertical-align: middle;
  font-size: 12px;
  /* &:not(&:last-child){
        margin-right:5px;
    } */
}
</style>
